using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using RaisingStudio.SmallBasicCompiler.Expressions;
using RaisingStudio.SmallBasicCompiler.Statements;
using RaisingStudio.SmallBasicCompiler;

namespace RaisingStudio.SmallBasicCompiler
{
    public class SemanticAnalyzer
    {
        private Parser _parser;
        private SymbolTable _symbolTable;
        private TypeInfoBag _typeInfoBag;
        public SemanticAnalyzer(Parser parser, TypeInfoBag typeInfoBag)
        {
            if (parser == null)
            {
                throw new ArgumentNullException("parser");
            }
            if (typeInfoBag == null)
            {
                throw new ArgumentNullException("typeInfoBag");
            }
            this._parser = parser;
            this._symbolTable = this._parser.SymbolTable;
            this._typeInfoBag = typeInfoBag;
        }

        public void Analyze()
        {
            foreach (Statement current in this._parser.ParseTree)
            {
                this.AnalyzeStatement(current);
            }
            if (this._parser.Errors.Count == 0)
            {
                foreach (KeyValuePair<string, TokenInfo> current2 in this._symbolTable.Variables)
                {
                    if (!this._symbolTable.InitializedVariables.ContainsKey(current2.Key))
                    {
                        this._parser.AddError(current2.Value, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("VariableNotInitialized"), new object[]
						{
							current2.Value.Text
						}));
                    }
                }
            }
        }
        
        private void AnalyzeExpression(Expression expression, bool leaveValueInStack, bool mustBeAssignable)
        {
            Type type = expression.GetType();
            if (type == typeof(BinaryExpression))
            {
                this.AnalyzeBinaryExpression(expression as BinaryExpression, leaveValueInStack, mustBeAssignable);
                return;
            }
            if (type == typeof(ArrayExpression))
            {
                this.AnalyzeArrayExpression(expression as ArrayExpression, leaveValueInStack, mustBeAssignable);
                return;
            }
            if (type == typeof(IdentifierExpression))
            {
                this.AnalyzeIdentifierExpression(expression as IdentifierExpression, leaveValueInStack, mustBeAssignable);
                return;
            }
            if (type == typeof(MethodCallExpression))
            {
                this.AnalyzeMethodCallExpression(expression as MethodCallExpression, leaveValueInStack, mustBeAssignable);
                return;
            }
            if (type == typeof(NegativeExpression))
            {
                this.AnalyzeNegativeExpression(expression as NegativeExpression, leaveValueInStack, mustBeAssignable);
                return;
            }
            if (type == typeof(PropertyExpression))
            {
                this.AnalyzePropertyExpression(expression as PropertyExpression, leaveValueInStack, mustBeAssignable);
            }
        }
        private void AnalyzeStatement(Statement statement)
        {
            Type type = statement.GetType();
            if (type == typeof(AssignmentStatement))
            {
                this.AnalyzeAssignmentStatement(statement as AssignmentStatement);
                return;
            }
            if (type == typeof(ElseIfStatement))
            {
                this.AnalyzeElseIfStatement(statement as ElseIfStatement);
                return;
            }
            if (type == typeof(ForStatement))
            {
                this.AnalyzeForStatement(statement as ForStatement);
                return;
            }
            if (type == typeof(GotoStatement))
            {
                this.AnalyzeGotoStatement(statement as GotoStatement);
                return;
            }
            if (type == typeof(IfStatement))
            {
                this.AnalyzeIfStatement(statement as IfStatement);
                return;
            }
            if (type == typeof(MethodCallStatement))
            {
                this.AnalyzeMethodCallStatement(statement as MethodCallStatement);
                return;
            }
            if (type == typeof(SubroutineCallStatement))
            {
                this.AnalyzeSubroutineCallStatement(statement as SubroutineCallStatement);
                return;
            }
            if (type == typeof(SubroutineStatement))
            {
                this.AnalyzeSubroutineStatement(statement as SubroutineStatement);
                return;
            }
            if (type == typeof(WhileStatement))
            {
                this.AnalyzeWhileStatement(statement as WhileStatement);
            }
        }
        private void AnalyzeBinaryExpression(BinaryExpression binaryExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            if (binaryExpression.LeftHandSide != null)
            {
                this.AnalyzeExpression(binaryExpression.LeftHandSide, leaveValueInStack, mustBeAssignable);
            }
            if (binaryExpression.RightHandSide != null)
            {
                this.AnalyzeExpression(binaryExpression.RightHandSide, leaveValueInStack, mustBeAssignable);
            }
        }
        private void AnalyzeArrayExpression(ArrayExpression arrayExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            if (arrayExpression.LeftHand != null)
            {
                this.AnalyzeExpression(arrayExpression.LeftHand, leaveValueInStack, mustBeAssignable);
            }
            if (arrayExpression.Indexer != null)
            {
                this.AnalyzeExpression(arrayExpression.Indexer, leaveValueInStack, mustBeAssignable);
            }
        }
        private void AnalyzeIdentifierExpression(IdentifierExpression identifierExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            if (identifierExpression.Identifier.Token != Token.Illegal)
            {
                this.NoteVariableReference(identifierExpression.Identifier);
            }
        }
        private void AnalyzeMethodCallExpression(MethodCallExpression methodCallExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            this.NoteMethodCallReference(methodCallExpression, leaveValueInStack, mustBeAssignable);
            foreach (Expression current in methodCallExpression.Arguments)
            {
                this.AnalyzeExpression(current, leaveValueInStack, mustBeAssignable);
            }
        }
        private void AnalyzeNegativeExpression(NegativeExpression negativeExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            if (negativeExpression.Expression != null)
            {
                this.AnalyzeExpression(negativeExpression.Expression, leaveValueInStack, mustBeAssignable);
            }
        }
        private void AnalyzePropertyExpression(PropertyExpression propertyExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            this.NotePropertyReference(propertyExpression, leaveValueInStack, mustBeAssignable);
        }
        private void AnalyzeAssignmentStatement(AssignmentStatement assignmentStatement)
        {
            IdentifierExpression identifierExpression = assignmentStatement.RightValue as IdentifierExpression;
            if (identifierExpression != null && this._symbolTable.Subroutines.ContainsKey(identifierExpression.Identifier.NormalizedText))
            {
                this.NoteEventReference(assignmentStatement.LeftValue, identifierExpression.Identifier);
                return;
            }
            if (assignmentStatement.LeftValue != null)
            {
                this.AnalyzeExpression(assignmentStatement.LeftValue, false, true);
            }
            if (assignmentStatement.RightValue != null)
            {
                this.AnalyzeExpression(assignmentStatement.RightValue, true, false);
            }
        }
        private void AnalyzeElseIfStatement(ElseIfStatement elseIfStatement)
        {
            if (elseIfStatement.Condition != null)
            {
                this.AnalyzeExpression(elseIfStatement.Condition, true, false);
            }
            foreach (Statement current in elseIfStatement.ThenStatements)
            {
                this.AnalyzeStatement(current);
            }
        }
        private void AnalyzeForStatement(ForStatement forStatement)
        {
            if (forStatement.Iterator.Token != Token.Illegal)
            {
                this.NoteVariableReference(forStatement.Iterator);
            }
            if (forStatement.InitialValue != null)
            {
                this.AnalyzeExpression(forStatement.InitialValue, true, false);
            }
            if (forStatement.FinalValue != null)
            {
                this.AnalyzeExpression(forStatement.FinalValue, true, false);
            }
            if (forStatement.StepValue != null)
            {
                this.AnalyzeExpression(forStatement.StepValue, true, false);
            }
            foreach (Statement current in forStatement.ForBody)
            {
                this.AnalyzeStatement(current);
            }
        }
        private void AnalyzeGotoStatement(GotoStatement gotoStatement)
        {
            if (gotoStatement.Label.Token != Token.Illegal)
            {
                this.NoteLabelReference(gotoStatement.Label);
            }
        }
        private void AnalyzeIfStatement(IfStatement ifStatement)
        {
            if (ifStatement.Condition != null)
            {
                this.AnalyzeExpression(ifStatement.Condition, true, false);
            }
            foreach (Statement current in ifStatement.ThenStatements)
            {
                this.AnalyzeStatement(current);
            }
            foreach (Statement current2 in ifStatement.ElseIfStatements)
            {
                this.AnalyzeStatement(current2);
            }
            foreach (Statement current3 in ifStatement.ElseStatements)
            {
                this.AnalyzeStatement(current3);
            }
        }
        private void AnalyzeMethodCallStatement(MethodCallStatement methodCallStatement)
        {
            if (methodCallStatement.MethodCallExpression != null)
            {
                this.AnalyzeExpression(methodCallStatement.MethodCallExpression, false, false);
            }
        }
        private void AnalyzeSubroutineCallStatement(SubroutineCallStatement subroutineCallStatement)
        {
            if (subroutineCallStatement.SubroutineName.Token != Token.Illegal)
            {
                this.NoteSubroutineCall(subroutineCallStatement.SubroutineName);
            }
        }
        private void AnalyzeSubroutineStatement(SubroutineStatement subroutineStatement)
        {
            foreach (Statement current in subroutineStatement.SubroutineBody)
            {
                this.AnalyzeStatement(current);
            }
        }
        private void AnalyzeWhileStatement(WhileStatement whileStatement)
        {
            if (whileStatement.Condition != null)
            {
                this.AnalyzeExpression(whileStatement.Condition, true, false);
            }
            foreach (Statement current in whileStatement.WhileBody)
            {
                this.AnalyzeStatement(current);
            }
        }

        private void NoteEventReference(Expression leftValue, TokenInfo subroutineName)
        {
            PropertyExpression propertyExpression = leftValue as PropertyExpression;
            if (propertyExpression != null)
            {
                TokenInfo typeName = propertyExpression.TypeName;
                TokenInfo propertyName = propertyExpression.PropertyName;
                if (typeName.Token == Token.Illegal || propertyName.Token == Token.Illegal)
                {
                    return;
                }
                TypeInfo typeInfo = null;
                if (!this._typeInfoBag.Types.TryGetValue(typeName.NormalizedText, out typeInfo))
                {
                    this._parser.AddError(typeName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("TypeNotFound"), new object[]
					{
						typeName.Text
					}));
                    return;
                }
                if (!typeInfo.Events.ContainsKey(propertyName.NormalizedText))
                {
                    this._parser.AddError(propertyName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("EventNotFound"), new object[]
					{
						propertyName.Text, 
						typeName.Text
					}));
                    return;
                }
            }
            else
            {
                this._parser.AddError(subroutineName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("SubroutineEventAssignment"), new object[]
				{
					subroutineName.Text
				}));
            }
        }
        private void NoteLabelReference(TokenInfo label)
        {
            if (label.Token == Token.Illegal)
            {
                return;
            }
            if (!this._symbolTable.Labels.ContainsKey(label.NormalizedText))
            {
                this._parser.AddError(label, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("LabelNotFound"), new object[]
				{
					label.Text
				}));
            }
        }
        private void NoteMethodCallReference(MethodCallExpression methodExpression, bool leaveValueInStack, bool isAssignable)
        {
            TokenInfo typeName = methodExpression.TypeName;
            TokenInfo methodName = methodExpression.MethodName;
            if (typeName.Token == Token.Illegal || methodName.Token == Token.Illegal)
            {
                return;
            }
            TypeInfo typeInfo = null;
            if (this._typeInfoBag.Types.TryGetValue(typeName.NormalizedText, out typeInfo))
            {
                MethodInfo methodInfo = null;
                if (!typeInfo.Methods.TryGetValue(methodName.NormalizedText, out methodInfo))
                {
                    this._parser.AddError(methodName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("MethodNotFound"), new object[]
					{
						methodName.Text, 
						typeName.Text
					}));
                    return;
                }
                int num = methodInfo.GetParameters().Length;
                if (num != methodExpression.Arguments.Count)
                {
                    this._parser.AddError(methodName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("ArgumentNumberMismatch"), new object[]
					{
						typeName.Text, 
						methodName.Text, 
						methodExpression.Arguments.Count, 
						num
					}));
                }
                if (leaveValueInStack && methodInfo.ReturnType == typeof(void))
                {
                    this._parser.AddError(methodName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("ReturnValueExpectedFromVoidMethod"), new object[]
					{
						typeName.Text, 
						methodName.Text
					}));
                    return;
                }
            }
            else
            {
                this._parser.AddError(typeName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("TypeNotFound"), new object[]
				{
					typeName.Text
				}));
            }
        }
        private void NotePropertyReference(PropertyExpression propertyExpression, bool leaveValueInStack, bool mustBeAssignable)
        {
            TokenInfo typeName = propertyExpression.TypeName;
            TokenInfo propertyName = propertyExpression.PropertyName;
            if (typeName.Token == Token.Illegal || propertyName.Token == Token.Illegal)
            {
                return;
            }
            TypeInfo typeInfo = null;
            if (this._typeInfoBag.Types.TryGetValue(typeName.NormalizedText, out typeInfo))
            {
                PropertyInfo propertyInfo = null;
                if (!typeInfo.Properties.TryGetValue(propertyName.NormalizedText, out propertyInfo))
                {
                    this._parser.AddError(propertyName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("PropertyNotFound"), new object[]
					{
						propertyName.Text, 
						typeName.Text
					}));
                    return;
                }
                if (mustBeAssignable && !propertyInfo.CanWrite)
                {
                    this._parser.AddError(propertyName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("PropertyIsReadOnly"), new object[]
					{
						propertyName.Text, 
						typeName.Text
					}));
                }
                if (leaveValueInStack && !propertyInfo.CanRead)
                {
                    this._parser.AddError(propertyName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("PropertyIsWriteOnly"), new object[]
					{
						propertyName.Text, 
						typeName.Text
					}));
                    return;
                }
            }
            else
            {
                this._parser.AddError(typeName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("TypeNotFound"), new object[]
				{
					typeName.Text
				}));
            }
        }
        private void NoteSubroutineCall(TokenInfo subroutineName)
        {
            if (subroutineName.Token == Token.Illegal)
            {
                return;
            }
            if (!this._symbolTable.Subroutines.ContainsKey(subroutineName.NormalizedText))
            {
                this._parser.AddError(subroutineName, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("SubroutineNotDefined"), new object[]
				{
					subroutineName.Text
				}));
            }
        }
        private void NoteVariableReference(TokenInfo variable)
        {
            if (variable.Token == Token.Illegal)
            {
                return;
            }
            if (!this._symbolTable.Variables.ContainsKey(variable.NormalizedText))
            {
                bool flag = this._symbolTable.Subroutines.ContainsKey(variable.NormalizedText);
                if (flag)
                {
                    this._parser.AddError(variable, string.Format(CultureInfo.CurrentUICulture, ResourceHelper.GetString("SubroutineUsedAsVariable"), new object[]
					{
						variable.Text
					}));
                }
            }
        }
    }
}
